
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

function Widgeon () {
    this.utils = Components.classes['@berjon.com/utils;1'].getService().wrappedJSObject;
    this.utils.widgeon = this;
    this.widgetCache = {};
    this.profileDir = Components.classes["@mozilla.org/file/directory_service;1"]
                         .getService(Components.interfaces.nsIProperties)
                         .get("ProfD", Components.interfaces.nsIFile);
    this.widDir = this.profileDir.clone();
    this.widDir.append("widgets");
    if (!this.widDir.exists()) this.widDir.create(this.widDir.DIRECTORY_TYPE, 0777); // XXXX perms
    
    // init DB
    var dbFile = this.profileDir.clone();
    dbFile.append("widgets.sqlite");
    var mustCreate = false;
    if (!dbFile.exists()) mustCreate = true;
    this.widgetsDB = Components.classes["@mozilla.org/storage/service;1"]
                               .getService(Components.interfaces.mozIStorageService)
                               .openDatabase(dbFile);
    if (mustCreate) {
        this.widgetsDB.executeSimpleSQL("CREATE TABLE widgets (id INTEGER PRIMARY KEY, uuid TEXT, config TEXT);");
    }
    this.wrappedJSObject = this;
}

Widgeon.prototype = {
    classDescription: "Widgeon — It Quacks Like A Widget Runtime",
    classID:          Components.ID("{65feff2d-c5ad-4001-8d2f-c9bce6891802}"),
    contractID:       "@berjon.com/widgeon;1",
    QueryInterface:   XPCOMUtils.generateQI(),
    
    supportsType:   function (type) {
        type = type.toLowerCase();
        if (type == "text/html" || type == "text/xml" || type == "application/xml") return true;
        if (/\+xml$/.test(type)) return true;
        return false;
    },
    
    // XXX this is probably incorrect, we'll have to figure it out later
    supportsEncoding:   function (enc) {
        enc = enc.toLowerCase();
        if (enc == "utf-8" || enc == "utf8" || enc == "utf-16" || enc == "utf16") return true;
        if (/^iso-8859-/.test(enc)) return true;
        return false;
    },
    
    installWidget:    function (file) {
        var u = this.utils;
        var uuid = Components.classes["@mozilla.org/uuid-generator;1"]
                             .getService(Components.interfaces.nsIUUIDGenerator)
                             .generateUUID()
                             .toString();
        uuid = uuid.replace("{", "").replace("}", "");
    
        // copy to location, then validate
        file.copyTo(this.widDir, uuid + ".wgt");
        var widget = this.createWidget(uuid);
        var config;
        try {
            config = widget.config();
        }
        catch (e) {
            u.error("Invalid widget package: " + e);
            this.removeWidget(uuid);
            return null;
        }
    
        // XXX note that config depends on locale, and will therefore need to be updated when locale is
        var stm = this.widgetsDB.createStatement("INSERT INTO widgets (uuid, config) VALUES(:uuid, :config)");
        stm.params.uuid = uuid;
        stm.params.config = this.utils.nativeJSON.encode(config);
        stm.execute();
        
        return widget;
    },
    
    removeWidget:    function (uuid) {
        var widFile = this.widDir.clone();
        widFile.append(uuid + ".wgt");
        widFile.remove(false);
        var stm = this.widgetsDB.createStatement("DELETE FROM widgets WHERE uuid = :uuid;");
        stm.params.uuid = uuid;
        stm.execute();
    },
    
    createWidget:    function (uuid) {
        var cw = this.hitCache(uuid);
        if (cw) return cw;
        var wid = Components.classes["@berjon.com/widget;1"]
                            .createInstance()
                            .wrappedJSObject
                            .initWithUUID(uuid);
        this.cacheWidget(uuid, wid);
        return wid;
    },
    
    cacheWidget:    function (uuid, widget) {
        this.widgetCache[uuid.toLowerCase()] = widget;
    },
    
    hitCache:    function (uuid) {
        if (!this.widgetCache[uuid.toLowerCase()]) return null;
        return this.widgetCache[uuid.toLowerCase()];
    },
    
    getPackage:    function (uuid) {
        var widFile = this.widDir.clone();
        widFile.append(uuid + ".wgt");
        var zReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]
                                .createInstance(Components.interfaces.nsIZipReader);
        zReader.open(widFile);
        return zReader;
    },
    
    getWidgetList:    function () {
        var stm = this.widgetsDB.createStatement("SELECT uuid FROM widgets;");
        // stm.execute();
        var res = [];
        while (stm.executeStep()) {  
            res.push(this.createWidget(stm.row.uuid));
        }
        return res;
    }
};

var components = [Widgeon];
function NSGetModule (compMgr, fileSpec) {
    return XPCOMUtils.generateModule(components);
}
